
#include <cstdio>
#include <BFile>
#include <BFolder>
#include <BPainter>
#include <BStyle>
#include <BFont>
#include <BCursor>
#include <BMessageBox>
#include <BFileDialog>
#include <BReadWrite>
#include <BGridLayout>
#include <BHBoxLayout>
#include <BVBoxLayout>
#include <BCamera>
#include <BViewer>
#include <BRoundHandler>
#include <BXmlDocument>
#include <BDrag>
#include <BSystem>
#include <BShortcut>
#include <BApplication>

#include "classes/ClassConsole.h"
#include "classes/ClassViewer.h"
#include "classes/ClassObject.h"
#include "classes/ClassNode.h"
#include "classes/ClassCamera.h"
#include "classes/ClassColor.h"
#include "classes/ClassVector.h"

#include "OutputWidget.h"
#include "HistoryDialog.h"
#include "StringUtils.h"
#include "InvokeBoard.h"
#include "VariantBoard.h"
#include "VariantBox.h"
#include "ScriptStudio.h"

ScriptStudio::ScriptStudio()
{
	_breakTextEdit = 0;
	_breakLineNumber = 0;

	_variantButton = new BButton(this);
	_variantButton->hide();

	_scriptThread = new ScriptThread();

	_itemMenu = new BMenu();
	{
		_openAction = new BAction("edit");
		_buildAction = new BAction("build");
		_addMenu = new BMenu("add");
		{
			_addFileAction = _addMenu->addAction("file");
			_addFolderAction = _addMenu->addAction("folder");
			_addScriptAction = _addMenu->addAction("script");
			connect(_addFileAction, Signal_Triggered, &ScriptStudio::slotAddFileActionTrigger);
			connect(_addFolderAction, Signal_Triggered, &ScriptStudio::slotAddFolderActionTrigger);
			connect(_addScriptAction, Signal_Triggered, &ScriptStudio::slotAddScriptActionTrigger);
		}
		_newMenu = new BMenu("new");
		{
			_newFileAction = _newMenu->addAction("file");
			_newFolderAction = _newMenu->addAction("folder");
			_newScriptAction = _newMenu->addAction("script");
			connect(_newFileAction, Signal_Triggered, &ScriptStudio::slotNewFileActionTrigger);
			connect(_newFolderAction, Signal_Triggered, &ScriptStudio::slotNewFolderActionTrigger);
			connect(_newScriptAction, Signal_Triggered, &ScriptStudio::slotNewScriptActionTrigger);
		}
		_removeAction = _itemMenu->addAction("remove");
		_removeAction->setShortcut("del");
		_browseAction = _itemMenu->addAction("browse");
		_settingAction = _itemMenu->addAction("setting");
		connect(_openAction, Signal_Triggered, &ScriptStudio::slotOpenActionTrigger);
		connect(_buildAction, Signal_Triggered, &ScriptStudio::slotBuildActionTriggered);
		connect(_removeAction, Signal_Triggered, &ScriptStudio::slotRemoveActionTrigger);
		connect(_browseAction, Signal_Triggered, &ScriptStudio::slotBrowseActionTrigger);
		connect(_settingAction, Signal_Triggered, &ScriptStudio::slotSettingActionTrigger);
	}

	_toolBar = new BWidget(this);
	{
		_openButton = new BButton("open");
		_openButton->setForm(Form_IconAlone);
		_openButton->setShortcut("ctrl+o");

		_saveButton = new BButton("save");
		_saveButton->setEnabled(false);
		_saveButton->setForm(Form_IconAlone);
		_saveButton->setShortcut("ctrl+s");

		_saveAllButton = new BButton("save-all");
		_saveAllButton->setForm(Form_IconAlone);
		_saveAllButton->setShortcut("ctrl+shift+s");

		_undoButton = new BButton("undo");
		_undoButton->setForm(Form_IconAlone);
		_undoButton->setShortcut("CTRL+Z");

		_redoButton = new BButton("redo");
		_redoButton->setForm(Form_IconAlone);
		_redoButton->setShortcut("CTRL+Y");

		_searchButton = new BButton("search");
		_searchButton->setCheckable(true);
		_searchButton->setForm(Form_IconAlone);
		_searchButton->setShortcut("CTRL+F");

		_viewButton = new BButton("view");
		_viewButton->setForm(Form_IconAlone);
		{
			BMenu *showPunctMenu = new BMenu("View");
			showPunctMenu->setName("view");
			showPunctMenu->addAction("show-tabs")->setCheckable(true);
			showPunctMenu->addAction("show-spaces")->setCheckable(true);
			showPunctMenu->addAction("show-returns")->setCheckable(true);
			showPunctMenu->addAction("show-linefeed")->setCheckable(true);
			_viewButton->setMenu(showPunctMenu);
			connect(showPunctMenu, Signal_Checked, &ScriptStudio::slotShowPunctMenuChecked);
			connect(showPunctMenu, Signal_Triggered, &ScriptStudio::slotShowPunctMenuTriggered);
		}

		_buildButton = new BButton("build");
		_buildButton->setCheckable(true);
		_buildButton->setForm(Form_IconAlone);
		_buildButton->setShortcut("CTRL+B");

		_callButton = new BSplitButton();
		_callButton->setCheckable(true);
		_callButton->setShortcut("Alt+F5");
		_callButton->addItem("Local debug");
		_callButton->addItem("Local run");
		_callButton->addItem("Thread debug");
		_callButton->addItem("Thread run");

		_debugLayout = new BHBoxLayout();
		_debugLayout->setSpacing(3);
		{
			_debugResetButton = new BButton("reset");
			_debugResetButton->setForm(Form_IconAlone);

			_debugRunButton = new BButton("run");
			_debugRunButton->setForm(Form_IconAlone);
			_debugRunButton->setShortcut("F5");

			_debugNextButton = new BButton("next");
			_debugNextButton->setForm(Form_IconAlone);
			_debugNextButton->setShortcut("F10");

			_debugStepButton = new BButton("step");
			_debugStepButton->setForm(Form_IconAlone);
			_debugStepButton->setShortcut("F11");

			_debugLayout->addWidget(_debugResetButton);
			_debugLayout->addWidget(_debugRunButton);
			_debugLayout->addWidget(_debugNextButton);
			_debugLayout->addWidget(_debugStepButton);
			_debugLayout->addSpring();
		}

		_paramsLayout = new BHBoxLayout();
		{
			_functionComboBox = new BComboBox();
			_functionComboBox->setMinWidth(200);

			_paramsLayout->addWidget(_functionComboBox);
		}

		BHBoxLayout *hlayout = new BHBoxLayout(_toolBar);
		hlayout->setSpacing(3);
		hlayout->addWidget(_openButton);
		hlayout->addWidget(_saveButton);
		hlayout->addWidget(_saveAllButton);
		hlayout->addSpacer(6);
		hlayout->addWidget(_undoButton);
		hlayout->addWidget(_redoButton);
		hlayout->addSpacer(6);
		hlayout->addWidget(_searchButton);
		hlayout->addWidget(_viewButton);
		hlayout->addSpacer(6);
		hlayout->addWidget(_buildButton);
		hlayout->addSpacer(6);
		hlayout->addWidget(_callButton);
		hlayout->addSpacer(6);
		hlayout->addLayout(_debugLayout);
		hlayout->addLayout(_paramsLayout);
		hlayout->addSpring();
	}

	_treeWidget = new BTreeWidget();
	_treeWidget->setTitle("Project");
	_treeWidget->setName("project");
	_treeWidget->setTitleVisible(true);
	{
		_removeAction->shortcut()->setWidget(_treeWidget);
		_itemMenu->setParent(_treeWidget);
		_itemEdit = new BLineEdit(_treeWidget);
		_itemEdit->hide();
	}

	_statusBoard = new StatusBoard("Status");
	_statusBoard->setTitleAlign(Align_Center);
	_statusBoard->setThread(_scriptThread);

	_editTabWidget = new BTabWidget();

	_bottomTabWidget = new BTabWidget();
	{
		OutputWidget *outputWidget = new OutputWidget();
		outputWidget->setReadOnly(true);
		outputWidget->setTitle("output");
		connect(outputWidget, Signal_DoubleClicked, &ScriptStudio::slotOutputDoubleClicked);

		InvokeBoard *invokeBoard = new InvokeBoard();
		invokeBoard->setTitle("invokes");

		VariantBoard *variantBoard = new VariantBoard();
		variantBoard->setTitle("variants");

		_searchBoard = new BTreeWidget();
		_searchBoard->setTitle("search");

		_bottomTabWidget->addTab(outputWidget, true);
		_bottomTabWidget->addTab(invokeBoard, true);
		_bottomTabWidget->addTab(variantBoard, true);
		_bottomTabWidget->addTab(_searchBoard, true);
	}

	BGridLayout *gridLayout = new BGridLayout();
	gridLayout->setSplitable(true);
	gridLayout->setSpacing(5);
	gridLayout->addWidget(0, 0, _treeWidget);
	gridLayout->addWidget(0, 1, _editTabWidget);
	gridLayout->addWidget(1, 0, _statusBoard);
	gridLayout->addWidget(1, 1, _bottomTabWidget);
	gridLayout->setColumnStretch(0, 1);
	gridLayout->setColumnStretch(1, 10);
	gridLayout->setRowStretch(0, 10);
	gridLayout->setRowStretch(1, 1);

	BVBoxLayout *layout = new BVBoxLayout(this);
	layout->addWidget(_toolBar, 0);
	layout->addLayout(gridLayout);

	connect(_openButton, Signal_Clicked, &ScriptStudio::slotOpenButtonClicked);
	connect(_saveButton, Signal_Clicked, &ScriptStudio::slotSaveButtonClicked);
	connect(_saveAllButton, Signal_Clicked, &ScriptStudio::slotSaveAllButtonClicked);
	connect(_undoButton, Signal_Clicked, &ScriptStudio::slotUndoButtonClicked);
	connect(_redoButton, Signal_Clicked, &ScriptStudio::slotRedoButtonClicked);
	connect(_searchButton, Signal_Toggled, &ScriptStudio::slotSearchButtonToggled);
	connect(_buildButton, Signal_Toggled, &ScriptStudio::slotBuildButtonToggled);
	connect(_callButton, Signal_Toggled, &ScriptStudio::slotCallButtonToggled);

	connect(_debugResetButton, Signal_Clicked, &ScriptStudio::slotDebugResetButtonClicked);
	connect(_debugRunButton, Signal_Clicked, &ScriptStudio::slotDebugRunButtonClicked);
	connect(_debugNextButton, Signal_Clicked, &ScriptStudio::slotDebugNextButtonClicked);
	connect(_debugStepButton, Signal_Clicked, &ScriptStudio::slotDebugStepButtonClicked);
	connect(_functionComboBox, Signal_ValueChanged, &ScriptStudio::slotDebugFunctionChanged);

	connect(_itemEdit, Signal_Focused, &ScriptStudio::slotItemEditFocused);
	connect(_itemEdit, Signal_TextEdited, &ScriptStudio::slotItemTextEdited);
	connect(_itemEdit, Signal_EditFinished, &ScriptStudio::slotItemEditFinished);

	connect(_treeWidget, Signal_ItemClicked, &ScriptStudio::slotTreeItemClicked);
	connect(_treeWidget, Signal_ItemDoubleClicked, &ScriptStudio::slotTreeItemDoubleClicked);
	connect(_treeWidget, Signal_SelectionChanged, &ScriptStudio::slotTreeSelectionChanged);
	connect(_treeWidget, Signal_RightClicked, &ScriptStudio::slotTreeWidgetRightClicked);
	connect(_treeWidget, Signal_Styled, &ScriptStudio::slotTreeWidgetStyled);
	connect(_treeWidget, Signal_Framed, &ScriptStudio::slotTreeWidgetFramed);
	connect(_treeWidget, Signal_DragDropped, &ScriptStudio::slotTreeWidgetDragDropped);
	connect(_editTabWidget, Signal_CurrentChanged, &ScriptStudio::slotCurrentTabChanged);

	connect("goto-definition", &ScriptStudio::slotGotoDefinition);
	connect("goto-declaration", &ScriptStudio::slotGotoDeclaration);

	_callButton->setEnabled(false);
	_debugResetButton->setEnabled(false);
	_debugRunButton->setEnabled(false);
	_debugNextButton->setEnabled(false);
	_debugStepButton->setEnabled(false);
}
ScriptStudio::~ScriptStudio()
{
	if (BScript *script = _callButton->asset())
	{
		if (script->running())
		{
			script->debug("stop");
		}
	}
}

bool ScriptStudio::loadProject(const BString &fileName)
{
	BSonDocument document;
	BSonNode *project = document.load(fileName);
	if (!project || project->name() != "project")
		return false;

	if (const BSonNode *configNode = project->child("config"))
	{
		BMenu *menu = _viewButton->menu();
		menu->action("show-tabs")->setChecked(configNode->attrib("show-tabs") == "true");
		menu->action("show-spaces")->setChecked(configNode->attrib("show-spaces") == "true");
		menu->action("show-returns")->setChecked(configNode->attrib("show-returns") == "true");
		menu->action("show-linefeed")->setChecked(configNode->attrib("show-linefeed") == "true");
	}
	BString message;
	BString filePath;
	for (int i = 0; i < project->childCount(); i++)
	{
		BSonNode *child = project->child(i);
		if (child->name() != "script")
			continue;
		filePath.reset();
		filePath << fileName.path() << '/' << child->attrib("file");
		BScriptHolder script = new BScript();
		initScript(script);
		if (!ReadFile(filePath, script))
		{
			message << "load script failed : " << filePath << '\n';
		}
		BString name = filePath.name();
		name -= name.dext();
		script->setName(name);
		script->setPath(fileName.path());
		script->setProperty("fileName", filePath);
		script->setProperty("entry", BEntry::Open(filePath));
		{
			BFolder *folder = new BFolder(fileName.path());
			folder->setNotifying(true);
			connect(folder, Signal_Notifying, &ScriptStudio::slotFolderNotifying);
			script->setProperty("folder", folder);
		}
		BTreeItem *item = createScriptItem(script);
		_treeWidget->addItem(item);
	}
	if (message.size())
	{
		BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
		output->addText(message);
	}
	_fileName = fileName;
	return true;
}
bool ScriptStudio::saveProject(const BString &fileName)
{
	BFile file(fileName);
	if (!file.open(IO_WriteText))
		return false;

	BSonNode *project = new BSonNode("project");
	if (BSonNode *configNode = project->addChild("config"))
	{
		configNode->setFormat("{\n;\n}\n");
		BMenu *menu = _viewButton->menu();
		if (menu->action("show-tabs")->checked())
			configNode->setAttrib("show-tabs", "true");
		if (menu->action("show-spaces")->checked())
			configNode->setAttrib("show-spaces", "true");
		if (menu->action("show-returns")->checked())
			configNode->setAttrib("show-returns", "true");
		if (menu->action("show-linefeed")->checked())
			configNode->setAttrib("show-linefeed", "true");
	}

	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		const BScript *script = _treeWidget->item(i)->asset();
		BString filePath = script->property("fileName");
		filePath.remove(fileName.path());
		filePath.removeHead('/');
		BSonNode *scriptNode = new BSonNode("script");
		scriptNode->setAttrib("file", filePath);
		project->addChild(scriptNode);
	}
	BSonDocument document;
	document.setFormat("{\n\n}\n");
	document.write(file, project);
	return true;
}

void print_info(int argc, BValue *argv)
{
	static int counter = 0;
	if (BScript *scirpt = argv[0])
	{
		int count = scirpt->query("counter");
		printf("\tcounter : %d.\n", count - counter);
		counter = count;
	}
}
void ScriptStudio::initScript(BScript *script)
{
	connect(script, Signal_Debugging, &ScriptStudio::slotScriptDebugging);
	connect(script, Signal_Compiled, &ScriptStudio::slotScriptCompiled);
	connect(script, Signal_Breaked, &ScriptStudio::slotScriptBreaked);
	connect(script, Signal_Inited, &ScriptStudio::slotScriptInited);
	connect(script, Signal_Error, &ScriptStudio::slotScriptError);

	script->registClass(new ClassConsole());
	script->registClass(new ClassViewer());
	script->registClass(new ClassObject());
	script->registClass(new ClassNode());
	script->registClass(new ClassCamera());
	script->registClass(new ClassColor());
	script->registClass(new ClassVector());
	script->registFunction("void print_info()", print_info);
	script->createVariant("real", "PI", PI);
	script->createVariant("real", "PI2", PI2);
}
BTreeItem *ScriptStudio::createScriptItem(BScript *script)
{
	BTreeItem *scriptItem = new BTreeItem(script->name());
	scriptItem->setAsset(script);
	AutoWorkPath workpath(script->path());
	for (int i = 0; i < script->sourceCount(); i++)
	{
		BSource *source = script->source(i);
		BTreeItem *item = createSourceItem(source);
		scriptItem->addChild(item);
	}
	return scriptItem;
}
BTreeItem *ScriptStudio::createSourceItem(BSource *source)
{
	BTreeItem *item = new BTreeItem(source->name());
	item->setAsset(source);
	const BString &fileName = source->fileName();
	SourceInfo *srcinfo = new SourceInfo();
	srcinfo->entry = BEntry::Open(fileName);
	if (srcinfo->entry)
	{
		BString text;
		BFile::Load(fileName, text);
		source->setText(text);
	}
	source->setFileName(fileName);
	source->setAsset(srcinfo);
	return item;
}
BScript *ScriptStudio::scriptOfFolder(BFolder *folder)
{
	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		BTreeItem *item = _treeWidget->item(i);
		if (BScript *script = item->asset())
		{
			if (script->property("folder") == folder)
				return script;
		}
	}
	return 0;
}
BWidget *ScriptStudio::openTreeItem(BTreeItem *item)
{
	BWidget *widget = _editTabWidget->tabOfAsset(item);
	if (!widget)
	{
		if (BSource *source = item->asset())
		{
			SourceEdit *sourceEdit = new SourceEdit();
			sourceEdit->setReadOnly(_debugRunButton->checked());
			sourceEdit->setAsset(source);
			widget = sourceEdit;
		}
		else if (BScript *script = item->asset())
		{
			ScriptBrowser *browser = new ScriptBrowser();
			browser->setAsset(script);
			browser->setReadOnly(true);
			widget = browser;
		}
		else if (BFile *file = item->asset())
		{
			BString text;
			if (!file->read(text))
			{
				BMessageBox::Critical("Error", "Load file failed.");
				return 0;
			}
			BTextEdit *edit = new BTextEdit();
			edit->setText(text);
			edit->setAsset(item->asset());
			edit->setReadOnly(_debugRunButton->checked());
			// connect(edit, Signal_Edited, &ScriptStudio::slotTextEdited);
			widget = edit;
		}
		widget->setTitle(item->text());
		widget->setIcon(item->icon());
		connect(widget, "create-breakpoint", &ScriptStudio::slotCreateBreakpoint);
		connect(widget, "remove-breakpoint", &ScriptStudio::slotRemoveBreakpoint);
		connect(widget, Signal_TextEdited, &ScriptStudio::slotSourceTextEdited);
		connect(widget, Signal_KeyPress, &ScriptStudio::slotDebugEditKeyPressed);
		connect(widget, Signal_Framed, &ScriptStudio::slotTextEditFramed);
		connect(widget, Signal_RightClicked, &ScriptStudio::slotTextEditRightClicked);
		_editTabWidget->addTab(widget, true);
		_editTabWidget->setTabAsset(widget, item);
	}
	_editTabWidget->setCurrent(widget);
	return widget;
}
void ScriptStudio::freshTreeItem(BTreeItem *item)
{
	BIcon *icon = 0;
	const BStyle *style = this->realStyle();
	if (BFolder *folder = item->asset())
	{
		icon = style->icon("filter");
	}
	else if (BScript *script = item->asset())
	{
		icon = style->icon("script");
	}
	else if (BSource *source = item->asset())
	{
		SourceInfo *srcinfo = source->asset();
		BString fileName = source->script()->path();
		fileName << '/' << source->name();
		icon = BSystem::EntryIcon(fileName);
	}
	item->setIcon(icon);
	for (int i = 0; i < item->childCount(); i++)
	{
		BTreeItem *child = item->child(i);
		freshTreeItem(child);
	}
}
void ScriptStudio::drawTreeItem(BPainter &painter, BTreeItem *item)
{
	if (BSource *source = item->asset())
	{
		SourceInfo *srcinfo = source->asset();
		if (srcinfo && srcinfo->entry.empty() && _lostImage)
		{
			BRect rect = item->iconRect().align(Align_LeftBottom, BSize(5, 5));
			painter.drawImage(rect, _lostImage, true);
		}
		return;
	}
	if (BScript *script = item->asset())
	{
		bool changed = script->property("changed");
		if (changed && _dirtyImage)
		{
			BRect rect = item->iconRect().align(Align_LeftBottom, BSize(5, 5));
			painter.drawImage(rect, _dirtyImage, true);
		}
		const BEntry *entry = script->property("entry");
		if (!entry && _lostImage)
		{
			BRect rect = item->iconRect().align(Align_LeftBottom, BSize(5, 5));
			painter.drawImage(rect, _lostImage, true);
		}
	}
	if (item->expanded())
	{
		for (int i = 0; i < item->childCount(); i++)
		{
			BTreeItem *child = item->child(i);
			drawTreeItem(painter, child);
		}
	}
}

void ScriptStudio::resetChanged(BWidget *widget)
{
	if (widget->title().endWith('*'))
	{
		BString title = widget->title();
		title -= '*';
		widget->setTitle(title);
	}
	if (_editTabWidget->current() == widget)
	{
		_saveButton->setEnabled(false);
	}
}
void ScriptStudio::signChanged(BWidget *widget)
{
	if (!widget->title().endWith('*'))
	{
		BString title = widget->title();
		title += '*';
		widget->setTitle(title);
	}
	if (_editTabWidget->current() == widget)
	{
		_saveButton->setEnabled(true);
	}
}

bool ScriptStudio::checkOpenAction(const BTreeItemArray &selection)
{
	if (selection.empty())
		return false;
	for (int i = 0; i < selection.size(); i++)
	{
		BTreeItem *item = selection.at(i);
		if (item->asset().is<BSource>())
			continue;
		if (item->asset().is<BScript>())
			continue;
		if (item->asset().is<BFile>())
			continue;
		return false;
	}
	return true;
}
bool ScriptStudio::checkBuildAction(const BTreeItemArray &selection)
{
	if (selection.empty())
		return false;
	for (int i = 0; i < selection.size(); i++)
	{
		BTreeItem *item = selection.at(i);
		if (item->asset().is<BSource>())
			continue;
		if (item->asset().is<BScript>())
			continue;
		return false;
	}
	return true;
}

bool ScriptStudio::saveFile()
{
	if (BWidget *widget = _editTabWidget->current())
	{
		if (!widget->title().endWith('*'))
			return false;
		if (BSource *source = widget->asset())
		{
			SourceInfo *srcinfo = source->asset();
			SourceEdit *edit = dynamic_cast<SourceEdit *>(widget);
			BString fileName = source->script()->path();
			fileName << '/' << source->name();
			if (BFile::Save(fileName, edit->text()))
			{
				dirtyDependSource(source->name());
				resetChanged(edit);
			}
			else
			{
				BMessageBox::Critical("Error : Save source file failed", fileName);
				return false;
			}
			for (int i = 0; i < srcinfo->exps.size(); i++)
			{
				collectDepends(srcinfo->exps[i], srcinfo);
			}
		}
	}
	return true;
}
bool ScriptStudio::saveAllFiles()
{
	for (int i = 0; i < _editTabWidget->tabCount(); i++)
	{
		BWidget *widget = _editTabWidget->tab(i);
		if (!widget->title().endWith('*'))
			continue;
		if (BSource *source = widget->asset())
		{
			SourceInfo *srcinfo = source->asset();
			SourceEdit *edit = dynamic_cast<SourceEdit *>(widget);
			BString fileName = source->script()->path();
			fileName << '/' << source->name();
			if (BFile::Save(fileName, edit->text()))
			{
				dirtyDependSource(source->name());
				resetChanged(edit);
			}
			else
			{
				BMessageBox::Critical("Error : Save source file failed", fileName);
				return false;
			}
			for (int i = 0; i < srcinfo->exps.size(); i++)
			{
				collectDepends(srcinfo->exps[i], srcinfo);
			}
		}
	}
	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		BScript *script = _treeWidget->item(i)->asset();
		if (script->property("changed"))
		{
			const BString &fileName = script->property("fileName");
			if (WriteFile(fileName, script))
			{
				script->setProperty("changed", false);
			}
			else
			{
				BMessageBox::Critical("Error : Save script file failed", fileName);
				return false;
			}
		}
	}
	_treeWidget->fresh();
	if (_treeWidget->title().endWith('*'))
	{
		if (_fileName.empty())
		{
			_fileName = BFileDialog::SaveFile("Save project : choose file name", "", "*.bsp");
			if (_fileName.empty())
				return false;
		}
		if (saveProject(_fileName))
		{
			resetChanged(_treeWidget);
		}
	}
	return true;
}

bool ScriptStudio::startBuilding()
{
	if (_buildTaskMap.empty())
		return false;
	_buildTask = _buildTaskMap.begin();
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	_bottomTabWidget->setCurrent(output);
	output->setText("------------- start build -------------\n");
	_buildButton->setChecked(true);
	return true;
}
bool ScriptStudio::abortBuilding()
{
	if (!_buildButton->checked())
		return false;
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	_bottomTabWidget->setCurrent(output);
	int successCount = 0;
	int failedCount = 0;
	for (int i = 0; i < _buildTask.index(); i++)
	{
		auto it = _buildTaskMap.seek(i);
		if (*it)
			successCount++;
		else
			failedCount++;
	}
	int abortCount = _buildTaskMap.size() - _buildTask.index();
	BString message;
	message << "======== build sucess " << successCount << ", failed " << failedCount << " ========\n";
	output->addText(message);
	_buildButton->setChecked(false);

	return true;
}
bool ScriptStudio::finishBuilding()
{
	if (!_buildButton->checked())
		return false;
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	_bottomTabWidget->setCurrent(output);
	int successCount = 0;
	int failedCount = 0;
	for (auto it = _buildTaskMap.begin(); it != _buildTaskMap.end(); it++)
	{
		if (*it)
			successCount++;
		else
			failedCount++;
	}
	BString message;
	message << "======== build sucess " << successCount << ", failed " << failedCount << " ========\n";
	output->addText(message);
	_buildButton->setChecked(false);
	return true;
}

void ScriptStudio::collectDepends(BExp *exp, SourceInfo *srcinfo)
{
	if (exp->syntax == Syntax_Include)
	{
		BExp *fileExp = exp->params.first();
		if (fileExp && fileExp->syntax == Syntax_File)
		{
			srcinfo->depends.append(fileExp->name);
		}
	}
	for (int i = 0; i < exp->subs.size(); i++)
	{
		collectDepends(exp->subs[i], srcinfo);
	}
}
void ScriptStudio::dirtyDependSource(const BString &header)
{
	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		BTreeItem *topItem = _treeWidget->item(i);
		BScript *script = topItem->asset();
		for (int s = 0; i < script->sourceCount(); i++)
		{
			BSource *source = script->source(i);
			SourceInfo *srcinfo = source->asset();
			if (srcinfo->depends.contain(header))
			{
			}
		}
	}
}
void ScriptStudio::jumpToBreakpoint(BScript *script)
{
	if (_breakpoint.size())
	{
		int pos = _breakpoint.find(':');
		if (pos < _breakpoint.size())
		{
			BString fileName = _breakpoint.substr(0, pos);
			BString numberStr = _breakpoint.substr(pos + 1, _breakpoint.size());
			_breakLineNumber = numberStr.toInt();
			BTreeItem *item = _treeWidget->itemOfText(fileName);
			_breakTextEdit = dynamic_cast<BTextEdit *>(openTreeItem(item));
		}
		else
		{
			_breakLineNumber = _breakpoint.toInt();
			BTreeItem *item = _treeWidget->itemOfAsset(script);
			_breakTextEdit = dynamic_cast<BTextEdit *>(openTreeItem(item));
		}
	}
	if (_breakTextEdit)
	{
		_editTabWidget->setCurrent(_breakTextEdit);
		_breakTextEdit->scrollToLine(_breakLineNumber);
		_breakTextEdit->setFocused(true);
		_breakTextEdit->fresh();
	}
}
void ScriptStudio::updateEntrance(BScript *script)
{
	if (script)
	{
		_functionComboBox->clearItems();
		_functionComboBox->addItem(BString());
		BString type, name;
		for (int i = 0; i < script->functionCount(); i++)
		{
			script->function(i, type, name);
			if (type.empty())
				type = "void";
			BComboItem *item = _functionComboBox->addItem(type + ' ' + name);
			item->setValue(name);
		}
	}
}
void ScriptStudio::freshScriptStack(BScript *script)
{
	if (VariantBoard *board = (VariantBoard *)_bottomTabWidget->tab("variants"))
	{
		board->setAsset(script);
		board->freshStack();
	}
	if (InvokeBoard *board = (InvokeBoard *)_bottomTabWidget->tab("invokes"))
	{
		board->setAsset(script);
		board->freshStack();
	}
}
BObject *ScriptStudio::createMenuOrAction(const BString &type, const BString &name, const BValue &value)
{
	const BValue *ptr = value;
	if (!ptr)
		ptr = &value;
	if (BClass *cls = value)
	{
		BString text;
		text << type << " : " << name;
		BMenu *menu = new BMenu(text);
		for (int i = 0; i < cls->variantCount(); i++)
		{
			BString type;
			BString name;
			const BValue &value = cls->variant(i, type, name);
			BObject *obj = createMenuOrAction(type, name, value);
			if (BMenu *child = dynamic_cast<BMenu *>(obj))
				menu->addMenu(child);
			else
				menu->addAction((BAction *)obj);
		}
		return menu;
	}
	BString text;
	if (ptr != &value)
		text << "*";
	if (type.empty())
		text << "void";
	else
		text << type;
	text << ' ' << name << " : " << StringOfValue(*ptr);
	return new BAction(text);
}

void ScriptStudio::slotOpenActionTrigger(BAction *action)
{
	if (BTreeItem *item = _treeWidget->selectedItem(0))
	{
		openTreeItem(item);
	}
}
void ScriptStudio::slotBuildActionTriggered(BAction *action)
{
	saveAllFiles();
	const BTreeItemArray &selection = _treeWidget->selection();
	if (selection.size())
	{
		_buildTaskMap.clear();
		for (int i = 0; i < selection.size(); i++)
		{
			BTreeItem *item = selection[i];
			_buildTaskMap.insert(item, false);
		}
		startBuilding();
	}
}
void ScriptStudio::slotAddFileActionTrigger(BAction *action)
{
	if (BTreeItem *item = _treeWidget->selectedItem())
	{
		BTreeItem *scriptItem = item->root();
		BScript *script = scriptItem->asset();
		const BString &fileName = script->property("fileName");
		BString path = fileName.path();
		if (!path.endWith('/'))
			path.append('/');
		BString filter("All files(*);");
		filter << "source file(*.cs);";
		filter << "header file(*.h);";
		BStringList fileNames = BFileDialog::OpenFiles("Add srcinfo file", path, filter);
		if (fileNames.size())
		{
			BString backup;
			BApplication::currentPath(backup);
			BApplication::setCurrentPath(path);
			for (int i = 0; i < fileNames.size(); i++)
			{
				BString fileName = fileNames[i];
				if (scriptItem->findByText(fileName.name()))
					continue;
				BSource *source = new BSource(fileName.name());
				SourceInfo *srcinfo = new SourceInfo();
				srcinfo->entry = BEntry::Open(fileName);
				if (srcinfo->entry)
				{
					BString text;
					BFile::Load(fileName, text);
					source->setText(text);
				}
				source->setAsset(srcinfo);
				source->setFileName(fileName);
				script->insertSource(source);
				fileName.remove(path);
				BTreeItem *child = scriptItem->create(fileName);
				child->setAsset(source);
				freshTreeItem(child);
			}
			BApplication::currentPath(backup);
			script->setProperty("changed", true);
		}
	}
}
void ScriptStudio::slotAddFolderActionTrigger(BAction *action)
{
	if (BTreeItem *parent = _treeWidget->selectedItem())
	{
		BTreeItem *scriptItem = parent->root();
		BScript *script = scriptItem->asset();
		const BString &fileName = script->property("fileName");
		BString path = fileName.path();
		if (!path.endWith('/'))
			path.append('/');
		BStringList fileNames = BFileDialog::OpenFiles("Add srcinfo file", path, "*.h;*.cpp");
		if (fileNames.empty())
			return;
		BString backup;
		BApplication::currentPath(backup);
		BApplication::setCurrentPath(path);
		for (int i = 0; i < fileNames.size(); i++)
		{
			BString fileName = fileNames[i];
			fileName.remove(path);
			BSource *source = new BSource(fileName);
			{
				SourceInfo *srcinfo = new SourceInfo();
				srcinfo->entry = BEntry::Open(fileName);
				if (srcinfo->entry)
				{
					BString text;
					BFile::Load(fileName, text);
					source->setText(text);
				}
				source->setAsset(srcinfo);
			}
			script->insertSource(source);

			BTreeItem *child = scriptItem->create(fileName);
			child->setAsset(source);
			freshTreeItem(child);
		}
		script->setProperty("changed", true);
	}
}
void ScriptStudio::slotAddScriptActionTrigger(BAction *action)
{
	BStringList fileNames = BFileDialog::OpenFiles("Add Script file", "", "*.obc;*.osc;*");
	if (fileNames.empty())
		return;
	for (int i = 0; i < fileNames.size(); i++)
	{
		const BString &fileName = fileNames[i];
		BString backup;
		BApplication::currentPath(backup);
		BApplication::setCurrentPath(fileName.path());
		BScript *script = new BScript();
		initScript(script);
		if (ReadFile(fileName, script))
		{
			script->setProperty("fileName", fileName);
			if (script->name().empty())
			{
				script->setName(fileName.name());
			}
			BTreeItem *scriptItem = createScriptItem(script);
			_treeWidget->addItem(scriptItem);
			freshTreeItem(scriptItem);
		}
		else
		{
			BMessageBox::Warning("Error", "Load script failed");
		}
		BApplication::currentPath(backup);
		signChanged(this);
	}
}
void ScriptStudio::slotNewFileActionTrigger(BAction *action)
{
	BString fileName("new-file");
	if (BTreeItem *parent = _treeWidget->selectedItem())
	{
		BScript *script = parent->root()->asset();
		AutoWorkPath workpath(script->path());
		if (BFolder *folder = parent->asset())
		{
			fileName.prepend("/");
			fileName.prepend(folder->path());
		}
		if (!BFile::Create(fileName))
		{
			return;
		}
		BSource *source = new BSource(fileName);
		source->setFileName(BFolder::AbsolutePath(fileName));
		script->insertSource(source);

		BTreeItem *item = createSourceItem(source);
		parent->addChild(item);
		freshTreeItem(item);
		openTreeItem(item);
	}
}
void ScriptStudio::slotNewFolderActionTrigger(BAction *action)
{
	if (BTreeItem *parent = _treeWidget->selectedItem())
	{
		BString filePath("new-folder");
		int number = 0;
		while (1)
		{
			if (parent && !parent->child(filePath))
				break;
			if (!parent && !_treeWidget->item(filePath))
				break;
			filePath.reset();
			filePath << "filter" << ++number;
		}
		BFolder *dir = parent->asset();
		filePath.prepend('/');
		filePath.prepend(dir->path());
		BFolder::Create(filePath);
		BFolder *folder = new BFolder(filePath);
		BTreeItem *item = new BTreeItem(filePath.name());
		parent->addChild(item);
		item->setAsset(folder);
		freshTreeItem(item);
	}
}
void ScriptStudio::slotNewScriptActionTrigger(BAction *action)
{
	BString fileName = BFileDialog::SaveFile("Create Script", "data", "*.obc;*.osc;*");
	if (fileName.size())
	{
		if (BFile::Exist(fileName))
		{
			BString message;
			message << fileName << '\n'
					<< "The file exists already.\nDo you want to overlap this file ?";
			if (Button_No == BMessageBox::Warning("Warning", message, Button_Yes | Button_No))
				return;
		}
		if (!BFile::Create(fileName))
		{
			BString message;
			message << fileName << '\n'
					<< "Create file failed : ";
			BMessageBox::Warning("Warning", message);
			return;
		}
		BScript *script = new BScript();
		initScript(script);
		script->setProperty("fileName", fileName);
		script->setName(fileName.name());
		WriteFile(fileName, script);
		BTreeItem *scriptItem = createScriptItem(script);
		_treeWidget->addItem(scriptItem);
		freshTreeItem(scriptItem);
		signChanged(this);
	}
}
void ScriptStudio::slotRemoveActionTrigger(BAction *action)
{
	BTreeItemArray selection = _treeWidget->selection();
	for (int i = 0; i < selection.size(); i++)
	{
		BTreeItem *item = selection[i];
		BScript *script = item->root()->asset();
		if (BSource *source = item->asset())
		{
			script->removeSource(source);
		}
		if (BFolder *folder = item->asset())
		{
			BFolder::Remove(folder->path());
		}
		BWidget *widget = _editTabWidget->tabOfAsset(item);
		_editTabWidget->removeTab(widget);
		if (BTreeItem *parent = item->parent())
		{
			parent->removeChild(item);
		}
		else
		{
			_treeWidget->removeItem(item);
		}
		script->setProperty("changed", true);
	}
}
void ScriptStudio::slotBrowseActionTrigger(BAction *action)
{
	if (BTreeItem *item = _itemMenu->asset())
	{
		BString path;
		if (BSource *source = item->asset())
		{
			path = source->name().path();
		}
		else if (BFolder *folder = item->asset())
			path = folder->path();
		BFileDialog::OpenFolder("", path);
	}
}
void ScriptStudio::slotSettingActionTrigger(BAction *action, const BValue &value)
{
	const BTreeItemArray &selection = _treeWidget->selection();
	if (selection.size() == 1)
	{
		BTreeItem *item = selection.first();
		if (BScript *script = item->asset())
		{
			static ScriptPropertyDialog *dialog = new ScriptPropertyDialog(this);
			dialog->setAsset(item->asset());
			dialog->popup();
		}
	}
}
void ScriptStudio::slotShowPunctMenuChecked(BMenu *menu, const BValue &value)
{
	BAction *action = value;
	BApplication::setProperty(action->text(), action->checked());
	for (int i = 0; i < _editTabWidget->tabCount(); i++)
	{
		_editTabWidget->tab(i)->fresh();
	}
}
void ScriptStudio::slotShowPunctMenuTriggered(BMenu *menu, const BValue &value)
{
	signChanged(_treeWidget);
}

void ScriptStudio::slotOpenButtonClicked()
{
	_fileName = BFileDialog::OpenFile("Open Script Project", "data", ".bsp");
	if (_fileName.empty())
		return;
	if (!_fileName.endWith(".bsp"))
	{
		BString text = "Open Project Failed";
		text << "\nscript project file should end with .bsp.";
		BMessageBox::Warning("Error", text);
		return;
	}
	if (loadProject(_fileName))
	{
		_treeWidget->setTitle(_fileName.name());
		_treeWidget->setIcon(BSystem::EntryIcon(_fileName));
		for (int i = 0; i < _treeWidget->itemCount(); i++)
		{
			freshTreeItem(_treeWidget->item(i));
		}
		this->emit("raise-item", _fileName);
	}
	else
	{
		BMessageBox::Warning("Warning", "Open Project Failed");
	}
}
void ScriptStudio::slotSaveButtonClicked()
{
	saveFile();
}
void ScriptStudio::slotSaveAllButtonClicked()
{
	saveAllFiles();
}
void ScriptStudio::slotUndoButtonClicked()
{
}
void ScriptStudio::slotRedoButtonClicked()
{
}
void ScriptStudio::slotSearchButtonToggled(BObject *object, const BValue &value)
{
	if (_searchDialog.empty())
	{
		_searchDialog = new SearchDialog(this);
		_searchDialog->setProperty("solution", _treeWidget);
		_searchDialog->setProperty("edit-tabs", _editTabWidget);
		_searchDialog->setProperty("results", _searchBoard);
		connect(_searchDialog, Signal_Hide, &ScriptStudio::slotSearchDialogHide);
	}
	if (value)
	{
		_searchDialog->setProperty("project", _callButton->asset());
		_searchDialog->setProperty("current-page", _editTabWidget->current());
		_searchDialog->setAsset(_editTabWidget->current());
		_searchDialog->show();
	}
	else
	{
		_searchDialog->hide();
	}
}
void ScriptStudio::slotSearchDialogHide(BObject *object, const BValue &value)
{
	_searchButton->setChecked(false);
}

void ScriptStudio::slotBuildButtonToggled(BObject *object, const BValue &value)
{
	bool checked = value;
	if (checked)
	{
		saveAllFiles();
		_buildTaskMap.clear();
		for (int i = 0; i < _treeWidget->itemCount(); i++)
		{
			BTreeItem *item = _treeWidget->item(i);
			_buildTaskMap.insert(item, false);
		}
		startBuilding();
	}
	else
	{
		abortBuilding();
	}
}
void ScriptStudio::slotCallButtonToggled(BObject *object, const BValue &value)
{
	BScript *script = _callButton->asset();
	_callButton->action();
	if (value == true)
	{
		BValueArray values;
		int retpos = _paramsLayout->index("return");
		if (VariantBox *varbox = dynamic_cast<VariantBox *>(_paramsLayout->widget(retpos - 1)))
		{
			values.append(varbox->value());
		}
		for (int i = retpos + 1; i < _paramsLayout->count(); i++)
		{
			VariantBox *varbox = (VariantBox *)_paramsLayout->widget(i);
			values.append(varbox->value());
		}
		bool debugging = _callButton->text().contain("debug");
		script->setDebugging(debugging);
		if (_callButton->text().contain("Local"))
		{
			script->call(_functionComboBox->value(), values.size(), values.data());
			if (VariantBox *varbox = dynamic_cast<VariantBox *>(_paramsLayout->widget(retpos - 1)))
			{
				varbox->setValue(values[0]);
			}
			for (int i = 1; i < values.size(); i++)
			{
				VariantBox *varbox = (VariantBox *)_paramsLayout->widget(retpos + i);
				varbox->setVariant(values[i]);
			}
			jumpToBreakpoint(script);
			freshScriptStack(script);
			if (_breakTextEdit)
			{
				_breakTextEdit->fresh();
				_breakpoint.clear();
				_breakTextEdit = 0;
				_breakLineNumber = 0;
			}
		}
		if (_callButton->text().contain("Thread"))
		{
			_paramsLayout->setEnabled(false);
			_scriptThread->setup(_functionComboBox->value(), values);
			_scriptThread->start();
			jumpToBreakpoint(script);
			freshScriptStack(script);
		}
	}
	else
	{
		script->setDebugging(false);
		if (_callButton->text().contain("Local"))
		{
			script->stop();
		}
		if (_callButton->text().contain("Thread"))
		{
			script->lock();
			script->stop();
			script->unlock();
		}
	}
}
void ScriptStudio::slotDebugResetButtonClicked(BObject *object, const BValue &value)
{
	BScript *script = _callButton->asset();
	script->reset();
	freshScriptStack(script);
}
void ScriptStudio::slotDebugRunButtonClicked(BObject *object, const BValue &value)
{
	BScript *script = _callButton->asset();
	bool res = script->debug(BString());
}
void ScriptStudio::slotDebugNextButtonClicked()
{
	BString command;
	int pos = _breakpoint.find(':');
	if (pos < _breakpoint.size())
	{
		BString numberStr = _breakpoint.substr(pos + 1, _breakpoint.size());
		int number = numberStr.toInt();
		command.append(_breakpoint, 0, pos + 1);
		command.append(number + 1);
	}
	else
	{
		int number = _breakpoint.toInt();
		command = number + 1;
	}
	BScript *script = _callButton->asset();
	bool res = script->debug(command);
}
void ScriptStudio::slotDebugStepButtonClicked()
{
	BScript *script = _callButton->asset();
	bool res = script->debug("step");
}
void ScriptStudio::slotDebugFunctionChanged(BObject *object, const BValue &value)
{
	while (_paramsLayout->count() > 1)
	{
		_toolBar->removeChild(_paramsLayout->widget(1));
	}
	const BString &text = _functionComboBox->text();
	if (text.size())
	{
		BString params = text.substr(text.find('(') + 1, text.rfind(')'));
		VariantBox *varbox = new VariantBox(text.word(0), false);
		_paramsLayout->addWidget(varbox);
		_paramsLayout->addWidget(new BWidget("return"));
		BStringArray types = params.split(',');
		for (int i = 0; i < types.size(); i++)
		{
			const BString &type = types[i];
			VariantBox *varbox = new VariantBox(type, true);
			_paramsLayout->addWidget(varbox);
		}
	}
}
void ScriptStudio::slotDebugEditKeyPressed(BTextEdit *textEdit, const BValue &value)
{
	Key key = value;
	if (key == Key_F9)
	{
		BScript *script = _callButton->asset();
		if (script->containBreakpoint(_breakpoint))
			script->removeBreakpoint(_breakpoint);
		else
			script->insertBreakpoint(_breakpoint);
		script->setProperty("changed", true);
		_treeWidget->fresh();
	}
}

void ScriptStudio::slotFolderNotifying(BFolder *folder, const BValue &value)
{
	Notify notify = value;
	if (notify == Notify_Rename)
	{
		BScript *script = scriptOfFolder(folder);
		for (int i = 0; i < script->sourceCount(); i++)
		{
			BSource *source = script->source(i);
			SourceInfo *srcinfo = source->asset();
			if (srcinfo->entry)
			{
				srcinfo->losted = !BEntry::Exist(srcinfo->entry->path());
			}
		}
	}
	if (notify == Notify_Access)
	{
		BScript *script = scriptOfFolder(folder);
		for (int i = 0; i < script->sourceCount(); i++)
		{
			BSource *source = script->source(i);
			SourceInfo *srcinfo = source->asset();
			if (srcinfo->entry)
			{
				srcinfo->losted = !BEntry::Exist(srcinfo->entry->path());
			}
		}
	}
}
void ScriptStudio::slotScriptDebugging(BObject *object, const BValue &value)
{
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	BScript *script = _callButton->asset();
	if (bool debugging = value)
	{
		for (int i = 0; i < _editTabWidget->tabCount(); i++)
		{
			BTextEdit *edit = dynamic_cast<BTextEdit *>(_editTabWidget->tab(i));
			edit->setReadOnly(true);
		}
		output->setText("------------- start debug -------------\n");
	}
	else
	{
		for (int i = 0; i < _editTabWidget->tabCount(); i++)
		{
			BTextEdit *edit = dynamic_cast<BTextEdit *>(_editTabWidget->tab(i));
			edit->setReadOnly(false);
		}
		output->addText("======== debug over ========\n");
	}
}
void ScriptStudio::slotScriptCompiled(BScript *script, const BValue &value)
{
	script->setProperty("changed", true);
}
void ScriptStudio::slotScriptMessage(BScript *script, const BValue &value)
{
	const BString &message = value;
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	int number = script->property("number");
	output->addText(number);
	output->addText('>');
	output->addText(message);
	if (!message.endWith('\n'))
	{
		output->addText('\n');
	}
}
void ScriptStudio::slotScriptBreaked(BScript *script, const BValue &value)
{
	_breakpoint = (const BString &)value;
	jumpToBreakpoint(script);
	freshScriptStack(script);
}
void ScriptStudio::slotScriptInited(BScript *script, const BValue &value)
{
	BWidget *widget = _bottomTabWidget->tab("output");
	script->setValue("console.board", widget);

	BViewer *viewer = new BViewer();
	{
		BCamera *camera = new BCamera();
		camera->setFovy(36);
		camera->setNear(1);
		camera->setFar(1000);
		viewer->setCamera(camera);

		viewer->setHandler(new BRoundHandler());
	}
	script->setValue("monitor.viewer", viewer);
	if (BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output"))
	{
		BString text = script->name();
		text << " : global variants inited.\n";
		output->addText(text);
	}
}
void ScriptStudio::slotScriptError(BScript *script, const BValue &value)
{
	const BString &message = value;
	if (_callButton->checked())
	{
		if (BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output"))
		{
			output->addText(message);
			if (!message.endWith('\n'))
			{
				output->addText('\n');
			}
		}
		message.substr(_breakpoint, 0, message.find(':'));
		jumpToBreakpoint(script);
		freshScriptStack(script);
		Button button = BMessageBox::Critical("Script Debug Error", message, Button_Cancel | Button_Close, Button_Close);
		if (button == Button_Close)
		{
			bool res = script->debug("stop");
		}
	}
	else
	{
		if (BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output"))
		{
			output->addText(message);
			if (!message.endWith('\n'))
			{
				output->addText('\n');
			}
		}
	}
}
void ScriptStudio::slotSourceMessage(BSource *source, const BValue &value)
{
	const BString &message = value;
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	int number = source->property("number");
	output->addText(number);
	output->addText('>');
	output->addText(message);
	if (!message.endWith('\n'))
	{
		output->addText('\n');
	}
}

void ScriptStudio::slotTreeWidgetStyled(BObject *object, const BValue &value)
{
	const BStyle *style = value;
	int size = style->font()->size();
	_treeWidget->setPerch(Part_Top, size + 5);
}
void ScriptStudio::slotTreeWidgetFramed(BObject *object, const BValue &value)
{
	BPainter painter(_treeWidget);
	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		BTreeItem *topItem = _treeWidget->item(i);
		drawTreeItem(painter, topItem);
	}
}
void ScriptStudio::slotTreeItemClicked(BObject *object, const BValue &value)
{
	BTreeItem *item = value;
	if (_itemEdit->asset() != item)
	{
		_itemEdit->setAsset(item);
		_pressedPos = BCursor::GetPos(_treeWidget);
		return;
	}
	if (item)
	{
		BPoint clickPos = BCursor::GetPos(_treeWidget);
		if ((clickPos - _pressedPos).length() > 5)
		{
			_itemEdit->setRect(item->textRect() + BSize(item->font()->size(), 0));
			_itemEdit->setText(item->text());
			_itemEdit->popup();
		}
		else
		{
			_itemEdit->setAsset(item);
			_pressedPos = clickPos;
		}
	}
}
void ScriptStudio::slotTreeItemDoubleClicked(BObject *object, const BValue &value)
{
	BTreeItem *item = value;
	if (item)
	{
		openTreeItem(item);
	}
}
void ScriptStudio::slotTreeSelectionChanged(BObject *object, const BValue &value)
{
	const BTreeItemArray &selection = _treeWidget->selection();
	_itemMenu->clear();
	if (checkOpenAction(selection))
		_itemMenu->addAction(_openAction);
	if (checkBuildAction(selection))
		_itemMenu->addAction(_buildAction);
	if (selection.empty())
	{
		_itemMenu->addMenu(_addMenu);
		_addMenu->action("file")->setEnabled(false);
		_addMenu->action("folder")->setEnabled(false);
		_addMenu->action("script")->setEnabled(true);
		_itemMenu->addMenu(_newMenu);
		_newMenu->action("file")->setEnabled(false);
		_newMenu->action("folder")->setEnabled(false);
		_newMenu->action("script")->setEnabled(true);
	}
	else if (selection.size() == 1)
	{
		BTreeItem *item = selection.first();
		if (item->asset().is<BScript>())
		{
			_itemMenu->addMenu(_addMenu);
			_addMenu->action("file")->setEnabled(true);
			_addMenu->action("folder")->setEnabled(true);
			_addMenu->action("script")->setEnabled(false);
			_itemMenu->addMenu(_newMenu);
			_newMenu->action("file")->setEnabled(true);
			_newMenu->action("folder")->setEnabled(true);
			_newMenu->action("script")->setEnabled(false);
		}
		else if (item->asset().is<BFolder>())
		{
			_itemMenu->addMenu(_addMenu);
			_addMenu->action("file")->setEnabled(true);
			_addMenu->action("folder")->setEnabled(false);
			_addMenu->action("script")->setEnabled(false);
			_itemMenu->addMenu(_newMenu);
			_newMenu->action("file")->setEnabled(true);
			_newMenu->action("folder")->setEnabled(false);
			_newMenu->action("script")->setEnabled(false);
		}
	}

	if (_treeWidget->selectedItemCount())
	{
		_itemMenu->addAction(_removeAction);
		_itemMenu->addAction(_browseAction);
	}
	_itemMenu->addAction(_settingAction);

	if (!_callButton->checked())
	{
		BScript *script = 0;
		if (selection.size() == 1 && selection.first()->selected())
			script = selection.first()->root()->asset();
		if (_callButton->asset() != script)
		{
			static BFontHolder font = new BFont(BApplication::font()->family(), 18);
			if (BTreeItem *scriptItem = _treeWidget->itemOfAsset(_callButton->asset()))
			{
				scriptItem->setFont(0);
				scriptItem->setChecked(false);
			}
			_callButton->setEnabled(script);
			_callButton->setAsset(script);
			updateEntrance(script);
			if (BTreeItem *scriptItem = _treeWidget->itemOfAsset(_callButton->asset()))
			{
				scriptItem->setFont(font);
				scriptItem->setChecked(true);
			}
			_statusBoard->setAsset(script);
			_scriptThread->setScript(script);
		}
	}
}
void ScriptStudio::slotTreeWidgetRightClicked(BObject *object, const BValue &value)
{
	const BPoint &pos = value;
	_itemMenu->setPos(pos);
	_itemMenu->popup();
}
void ScriptStudio::slotTreeWidgetDragDropped(BObject *object, const BValue &value)
{
	BDrag *drag = value;
	const BPoint &pos = drag->pos();
	BTreeItem *item = _treeWidget->itemAt(pos);
	BTreeItem *scriptItem = item;
	while (scriptItem)
	{
		BScript *script = scriptItem->asset();
		if (script)
			break;
		scriptItem = scriptItem->parent();
	}
	BTreeItem *folderItem = item;
	while (folderItem)
	{
		BFolder *folder = folderItem->asset();
		if (folder)
			break;
		folderItem = folderItem->parent();
	}
	BStringArray &fileNames = drag->value();
	if (BScript *script = scriptItem->asset())
	{
		for (int i = 0; i < fileNames.size(); i++)
		{
			const BString &fileName = fileNames[i];
			if (script->source(fileName.name()))
			{
				BString message = u8"�ļ��Ѿ����ڣ�\n";
				message << fileName.name();
				BMessageBox::Critical(this, message);
				return;
			}
		}
	}
	for (int i = 0; i < fileNames.size(); i++)
	{
		const BString &fileName = fileNames[i];
		BSource *source = new BSource(fileName.name());
		source->setFileName(fileName);
		BTreeItem *sourceItem = createSourceItem(source);
		if (BScript *script = scriptItem->asset())
		{
			script->setProperty("changed", true);
			script->insertSource(source);
			if (folderItem)
				sourceItem->addChild(sourceItem);
			else
				scriptItem->addChild(sourceItem);
		}
		else
		{
			_treeWidget->addItem(sourceItem);
		}
		freshTreeItem(sourceItem);
	}
}

void ScriptStudio::slotItemEditFocused(BObject *object, const BValue &value)
{
	if (value == false)
	{
		_itemEdit->setAsset(BValue());
		_itemEdit->hide();
	}
}
void ScriptStudio::slotItemTextEdited(BObject *object, const BValue &value)
{
	BValue temp;
	_itemEdit->query("text-size", temp);
	const BSize &textSize = temp;
	int minWidth = textSize.width() + _itemEdit->realFont()->size();
	BSize csize = _itemEdit->centerSize();
	if (csize.width() < minWidth)
	{
		csize.width() = minWidth;
		_itemEdit->setCenterSize(csize);
	}
}
void ScriptStudio::slotItemEditFinished(BObject *object, const BValue &value)
{
	BTreeItem *item = _itemEdit->asset();
	if (BSource *source = item->asset())
	{
		const BString &text = _itemEdit->text();
		if (text == item->text())
			return;
		SourceInfo *srcinfo = source->asset();
		SourceEdit *edit = (SourceEdit *)_editTabWidget->tab(item->text());
		BScript *script = source->script();
		AutoWorkPath workpath(script->path());
		BString name = source->name();
		if (srcinfo->entry)
		{
			if (BFile::Rename(name, text))
			{
				if (edit)
					edit->setTitle(text);
				name -= item->text();
				name << text;
				source->setName(name);
				script->setProperty("changed", true);
				item->setText(text);
				freshTreeItem(item);
			}
			else
			{
				BMessageBox::Warning("Error", "Rename source file failed");
			}
		}
		else
		{
			if (edit)
				edit->setTitle(text);
			name -= item->text();
			name << text;
			source->setName(name);
			srcinfo->entry = BEntry::Open(name);
			if (srcinfo->entry)
			{
				BString text;
				BFile::Load(name, text);
				source->setText(text);
				if (edit)
				{
					edit->setText(text);
				}
			}
			script->setProperty("changed", true);
			item->setText(text);
			freshTreeItem(item);
		}
		return;
	}
	if (BFolder *folder = item->asset())
	{
		BFolder::Rename(folder->path(), _itemEdit->text());
		return;
	}
}

void ScriptStudio::slotCurrentTabChanged(BObject *object, const BValue &value)
{
	BWidget *widget = value;
}
void ScriptStudio::slotSourceTextEdited(BObject *object, const BValue &vlaue)
{
	SourceEdit *edit = dynamic_cast<SourceEdit *>(object);
	BSource *source = edit->asset();
	SourceInfo *srcinfo = source->asset();
	this->signChanged(edit);
	_saveButton->setEnabled(true);
}
void ScriptStudio::slotTextEditFramed(BTextEdit *textEdit, const BValue &vlaue)
{
	BIntArray visualLines = textEdit->query("visual-lines");
	BIntSet breakLines = textEdit->query("break-lines");
	BRect breakArea = textEdit->query("break-area");
	BPainter painter(textEdit);
	painter.setColor(200, 128, 128);
	for (int i = 0; i < visualLines.size(); i++)
	{
		int line = visualLines[i];
		if (breakLines.contain(line))
		{
			BRect rect = textEdit->lineRect(line);
			rect.left() = breakArea.left();
			rect.right() = breakArea.right();
			rect.scale(rect.center(), 0.8);
			painter.drawGraph(rect, Graph_Circle_Fill);
		}
	}
	if (_breakTextEdit == textEdit)
	{
		BRect leftRect = textEdit->rect(Part_Left);
		BRect imageRect = textEdit->lineRect(_breakLineNumber);
		if (_arrowImage)
		{
			imageRect.left() = leftRect.right();
			imageRect.right() = leftRect.right() + imageRect.height();
			imageRect.setSize(imageRect.size().wrap(_arrowImage->size()));
			painter.drawImage(imageRect, _arrowImage);
		}
		else
		{
			imageRect.left() = leftRect.right();
			imageRect.right() = leftRect.right() + imageRect.height();
			painter.setColor(0, 100, 120);
			painter.drawGraph(imageRect.align(Align_RightCenter, imageRect.size() / 2), Graph_Arrow_Right_Fill);
		}
	}
}
void ScriptStudio::slotTextEditRightClicked(BTextEdit *textEdit, const BValue &vlaue)
{
	if (_editMenu.empty())
	{
		_editMenu = new BMenu();
		_editMenu->setName("edit");
	}
	_editMenu->clear();
	emit(textEdit, "fresh-menu", _editMenu);
	if (_editMenu->count())
	{
		BPoint pos = vlaue;
		_editMenu->setParent(textEdit);
		_editMenu->setPos(pos);
		_editMenu->popup();
	}
}
void ScriptStudio::slotOutputDoubleClicked(BObject *object, const BValue &value)
{
	BPoint pos = value;
	BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
	int line = output->lineAt(pos);
	BString text = output->lineText(line);
	int count = 0;
	int countEnd = text.find('>');
	if (countEnd < text.size())
	{
		count = text.toInt();
		text.remove(0, countEnd + 1);
	}
	int fileEnd = text.find(':');
	int end = text.find(':', fileEnd + 1);
	BString fileName = text.substr(0, fileEnd);
	fileName.trimmed();
	int rangeMin = text.find('(', fileEnd + 1, end);
	int rangeMax = text.find(')', rangeMin + 1, end);
	int lineNumber = 0;
	BRange range;
	if (rangeMin < end && rangeMax < end)
	{
		BString lineStr = text.substr(fileEnd + 1, rangeMin);
		BString rangeStr = text.substr(rangeMin + 1, rangeMax);
		lineNumber = lineStr.toInt();
		BStringArray words = rangeStr.split(',');
		if (words.size() == 2)
		{
			range.min() = words[0].toInt();
			range.max() = words[1].toInt();
		}
	}
	else
	{
		BString lineStr = text.substr(fileEnd + 1, end);
		lineNumber = lineStr.toInt();
	}
	BWidget *widget = 0;
	for (int ti = 0; ti < _editTabWidget->tabCount(); ti++)
	{
		BWidget *tab = _editTabWidget->tab(ti);
		if (tab->title() == fileName)
		{
			widget = tab;
		}
	}
	if (!widget)
	{
		BTreeItem *topItem = _treeWidget->item(count);
		BScript *script = topItem->asset();
		if (BTreeItem *item = topItem->locate(fileName))
		{
			widget = openTreeItem(item);
		}
	}
	_editTabWidget->setCurrent(widget);
	if (BTextEdit *textEdit = dynamic_cast<BTextEdit *>(widget))
	{
		textEdit->scrollToLine(lineNumber, Align_Center);
		textEdit->input("input-line", lineNumber);
		textEdit->input("input-range", range);
	}
}
void ScriptStudio::slotCreateBreakpoint(BObject *object, const BValue &value)
{
	if (BSource *source = object->asset())
	{
		int line = value;
		BString breakpoint = source->name();
		breakpoint << ':' << line;
		if (BScript *script = source->script())
		{
			script->insertBreakpoint(breakpoint);
			script->setProperty("changed", true);
		}
		return;
	}
	if (BScript *script = object->asset())
	{
		int line = value;
		script->insertBreakpoint(line);
		script->setProperty("changed", true);
		return;
	}
}
void ScriptStudio::slotRemoveBreakpoint(BObject *object, const BValue &value)
{
	if (BSource *source = object->asset())
	{
		int line = value;
		BString breakpoint = source->name();
		breakpoint << ':' << line;
		source->script()->removeBreakpoint(breakpoint);
		_saveButton->setEnabled(true);
		return;
	}
	if (BScript *script = object->asset())
	{
		int line = value;
		script->removeBreakpoint(line);
		_saveButton->setEnabled(true);
		return;
	}
}
void ScriptStudio::slotGotoDefinition(BObject *object, const BValue &value)
{
	if (BExp *exp = value)
	{
	}
}
void ScriptStudio::slotGotoDeclaration(BObject *object, const BValue &value)
{
	if (BExp *exp = value)
	{
		if (exp->syntax == Syntax_Type)
		{
			if (BScript *script = _callButton->asset())
			{
				for (int i = 0; i < script->sourceCount(); i++)
				{
					BSource *source = script->source(i);
				}
				script->findClass(exp->type);
			}
			for (int i = 0; i < _treeWidget->itemCount(); i++)
			{
				BTreeItem *item = _treeWidget->item(i);
				if (BScript *script = item->asset())
				{
				}
			}
		}
	}
}

void ScriptStudio::showEvent(const BEvent &event)
{
	BMainWindow::showEvent(event);
}
void ScriptStudio::closeEvent(const BEvent &event)
{
	if (_saveButton->enabled())
	{
		Button ret = BMessageBox::Question(this, "Animation Edit", "Animation content is changed, do you want save ?", Button_Save | Button_Cancel);
		if (ret == Button_Save)
		{
			slotSaveButtonClicked();
		}
		if (ret == Button_Cancel)
		{
			return;
		}
	}
	BMainWindow::closeEvent(event);
	BApplication::exit();
}

void ScriptStudio::resizeEvent(const BEvent &event)
{
	BMainWindow::resizeEvent(event);
}
void ScriptStudio::updateEvent(const BEvent &event)
{
	if (_buildButton->checked())
	{
		BTextEdit *output = (BTextEdit *)_bottomTabWidget->tab("output");
		if (_buildTask.valid())
		{
			int number = _buildTask.index();
			BTreeItem *item = _buildTask.key();
			if (BScript *script = item->asset())
			{
				BString message;
				message << number << ">-----" << item->text() << "-----\n";
				output->addText(message);

				script->setProperty("number", number);
				connect(script, Signal_Message, &ScriptStudio::slotScriptMessage);
				_buildTask.value() = script->compile();
				disconnect(script, Signal_Message);
				if (_callButton->asset() == script)
				{
					updateEntrance(script);
				}
				script->setProperty("changed", true);
			}
			else if (BSource *source = item->asset())
			{
				BString message;
				message << number << ">-----" << item->text() << "-----\n";
				output->addText(message);

				source->setProperty("number", number);
				connect(source, Signal_Message, &ScriptStudio::slotScriptMessage);
				_buildTask.value() = source->analyse();
				disconnect(source, Signal_Message);
			}
			_treeWidget->fresh();
			_buildTask++;
		}
		else
		{
			finishBuilding();
		}
	}

	if (BScript *script = _callButton->asset())
	{
		if (script->running())
		{
			_debugResetButton->setEnabled(false);
			_debugRunButton->setEnabled(true);
			_debugNextButton->setEnabled(true);
			_debugStepButton->setEnabled(true);
			_paramsLayout->setEnabled(false);
		}
		else
		{
			_debugResetButton->setEnabled(true);
			_debugRunButton->setEnabled(false);
			_debugNextButton->setEnabled(false);
			_debugStepButton->setEnabled(false);
			_paramsLayout->setEnabled(true);
		}
	}

	bool dirtySave = false;
	bool dirtyAllSave = _treeWidget->title().endWith('*');
	if (BWidget *current = _editTabWidget->current())
	{
		if (current->title().endWith('*'))
		{
			dirtySave = true;
			dirtyAllSave = true;
		}
	}
	if (!dirtyAllSave)
	{
		for (int i = 0; i < _editTabWidget->tabCount(); i++)
		{
			BWidget *widget = _editTabWidget->tab(i);
			if (widget->title().endWith('*'))
			{
				dirtyAllSave = true;
				break;
			}
		}
	}
	if (!dirtyAllSave)
	{
		for (int i = 0; i < _treeWidget->itemCount(); i++)
		{
			BScript *script = _treeWidget->item(i)->asset();
			if (script->property("changed"))
			{
				dirtyAllSave = true;
				break;
			}
		}
	}
	_saveButton->setEnabled(dirtySave);
	_saveAllButton->setEnabled(dirtyAllSave);
}

void ScriptStudio::styleEvent(const BEvent &event)
{
	BMainWindow::styleEvent(event);
	const BStyle *style = this->realStyle();
	_openButton->setIcon(style->icon("open"));
	_saveButton->setIcon(style->icon("save"));
	_saveAllButton->setIcon(style->icon("save-all"));
	_redoButton->setIcon(style->icon("redo"));
	_undoButton->setIcon(style->icon("undo"));
	_searchButton->setIcon(style->icon("search"));
	_viewButton->setIcon(style->icon("view"));
	_buildButton->setIcon(style->icon("build"));

	_callButton->item("Local debug")->setIcon(style->icon("debug"));
	_callButton->item("Local run")->setIcon(style->icon("call"));
	_callButton->item("Thread debug")->setIcon(style->icon("debug"));
	_callButton->item("Thread run")->setIcon(style->icon("call"));

	_debugResetButton->setIcon(style->icon("debug-reset"));
	_debugRunButton->setIcon(style->icon("debug-run"));
	_debugNextButton->setIcon(style->icon("debug-next"));
	_debugStepButton->setIcon(style->icon("debug-step"));

	_openButton->setTips(style->text("tips-open"));
	_saveButton->setTips(style->text("tips-save"));
	_searchButton->setTips(style->text("tips-search"));
	_buildButton->setTips(style->text("tips-build"));

	_debugResetButton->setTips(style->text("tips-debug-reset"));
	_debugRunButton->setTips(style->text("tips-debug-run"));
	_debugNextButton->setTips(style->text("tips-debug-next"));
	_debugStepButton->setTips(style->text("tips-debug-step"));

	_arrowImage = style->image("debug-arrow");
	_dirtyImage = style->image("file-dirty");
	_lostImage = style->image("file-lost");

	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		BTreeItem *topItem = _treeWidget->item(i);
		freshTreeItem(topItem);
	}
}
void ScriptStudio::freshEvent(const BEvent &event)
{
	BMainWindow::freshEvent(event);
}
void ScriptStudio::paintEvent(const BEvent &event)
{
	BMainWindow::paintEvent(event);
}
